﻿// Copyright 2008 Newgrove Consultants Limited
// By John Diss 
//
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using GeoAPI.Coordinates;
using GeoAPI.CoordinateSystems;
using GeoAPI.CoordinateSystems.Transformations;
using GeoAPI.Geometries;
using GisSharpBlog.NetTopologySuite.Geometries;
using NPack.Interfaces;
using ProjNet.CoordinateSystems;
using ProjNet.CoordinateSystems.Transformations;
using MsSqlCoordinate2D = NetTopologySuite.Coordinates.BufferedCoordinate;
using MsSqlCoordinateFactory = NetTopologySuite.Coordinates.BufferedCoordinateFactory;
using MsSqlCoordinateSequenceFactory = NetTopologySuite.Coordinates.BufferedCoordinateSequenceFactory;

public static class Global
{
    //public static IServices Services
    //{
    //    get
    //    {
    //        return ServiceFactory.CreateDefault();
    //    }
    //}

    public static readonly IServices Services;

    static Global()
    {
        Services = ServiceFactory.CreateDefault();
    }
    /// <remarks>Hopefully a temporary fix due to IGeometry.Srid changing from int? to string </remarks>
    public static int? ParseSridString(string srid)
    {
        int id;
        if (int.TryParse(srid, out id))
            return id;
        return null;
    }
}

///// <summary>
///// Not intended to be instantiated directly. Use Defaults{TCoordinate}.Services
///// </summary>
///// <typeparam name="TCoordinate"></typeparam>
///// 
internal class Services<TCoordinate> : IServices<TCoordinate>
    where TCoordinate : ICoordinate<TCoordinate>, IEquatable<TCoordinate>, IComparable<TCoordinate>,
        IComputable<Double, TCoordinate>, IConvertible
{
    private readonly ICoordinateFactory<TCoordinate> _coordinateFactory;
    private readonly ICoordinateSystemFactory<TCoordinate> _coordinateSystemFactory;
    private readonly IGeometryFactory<TCoordinate> _geometryFactory;

    private readonly Dictionary<int, IGeometryFactory<TCoordinate>> _sridAwareFactories =
        new Dictionary<int, IGeometryFactory<TCoordinate>>();

    private readonly IDictionary<TransformCacheKey, IMathTransform> _transformCache;
    public ICoordinateTransformationFactory<TCoordinate> _coordTransformationFactory;

    public Services(
        IDictionary<TransformCacheKey, IMathTransform> transformCache,
        ICoordinateFactory<TCoordinate> coordinateFactory,
        ICoordinateSystemFactory<TCoordinate> coordinateSystemFactory,
        IGeometryFactory<TCoordinate> geometryFactory,
        ICoordinateTransformationFactory<TCoordinate> coordTransformFactory)
    {
        _coordinateFactory = coordinateFactory;
        _transformCache = transformCache;
        _coordinateSystemFactory = coordinateSystemFactory;
        _geometryFactory = geometryFactory;
        _coordTransformationFactory = coordTransformFactory;
    }

    public IDictionary<TransformCacheKey, IMathTransform> TransformCache
    {
        get { return _transformCache; }
    }

    #region IServices<TCoordinate> Members

    public ICoordinateFactory<TCoordinate> CoordinateFactory
    {
        get { return _coordinateFactory; }
    }

    public IGeometryFactory<TCoordinate> GeometryFactory
    {
        get { return _geometryFactory; }
    }


    public ICoordinateSystemFactory<TCoordinate> CoordinateSystemFactory
    {
        get { return _coordinateSystemFactory; }
    }


    public IGeometryFactory<TCoordinate> SridAwareGeometryFactory(int? srid)
    {
        if (!srid.HasValue || srid <= 0)
            return GeometryFactory;


        lock (_sridAwareFactories)
        {
            if (!_sridAwareFactories.ContainsKey(srid.Value))
            {
                _sridAwareFactories.Add(srid.Value,
                                        new GeometryFactory<TCoordinate>(srid.ToString(), GeometryFactory.CoordinateSequenceFactory));
            }
        }
        return _sridAwareFactories[srid.Value];
    }

    public IGeometryFactory<TCoordinate> this[int? srid]
    {
        get { return SridAwareGeometryFactory(srid); }
    }

    public IGeometryFactory<TCoordinate> this[string srid]
    {
        get { return SridAwareGeometryFactory(Global.ParseSridString(srid)); }
    }

    IGeometryFactory IServices.this[string srid]
    {
        get { return this[srid]; }
    }

    ICoordinateFactory IServices.CoordinateFactory
    {
        get { return CoordinateFactory; }
    }

    ICoordinateSystemFactory IServices.CoordinateSystemFactory
    {
        get { return CoordinateSystemFactory; }
    }

    IGeometryFactory IServices.GeometryFactory
    {
        get { return GeometryFactory; }
    }

    IGeometryFactory IServices.SridAwareGeometryFactory(int? srid)
    {
        return SridAwareGeometryFactory(srid);
    }

    IGeometryFactory IServices.this[int? srid]
    {
        get { return this[srid]; }
    }

    public ICoordinateTransformationFactory<TCoordinate> CoordinateTransformationFactory
    {
        get { return _coordTransformationFactory; }
    }

    ICoordinateTransformationFactory IServices.CoordinateTransformationFactory
    {
        get { return CoordinateTransformationFactory; }
    }

    public IGeometryFactory<TCoordinate> this[SqlInt32 srid]
    {
        get { return SridAwareGeometryFactory(srid); }
    }

    IGeometryFactory IServices.this[SqlInt32 srid]
    {
        get { return this[srid]; }
    }

    #endregion

    public IGeometryFactory<TCoordinate> SridAwareGeometryFactory(SqlInt32 srid)
    {
        if (srid.IsNull || srid <= 0)
            return GeometryFactory;
        return SridAwareGeometryFactory((int?)srid.Value);
    }
}


internal static class ServiceFactory
{
    public static IServices CreateDefault()
    {
        ICoordinateFactory<MsSqlCoordinate2D> coordFactory
            = new MsSqlCoordinateFactory();
        ICoordinateSequenceFactory<MsSqlCoordinate2D> coordSequenceFactory
            = new MsSqlCoordinateSequenceFactory((MsSqlCoordinateFactory)coordFactory);
        IGeometryFactory<MsSqlCoordinate2D> geometryFactory
            = new GeometryFactory<MsSqlCoordinate2D>(coordSequenceFactory);
        ICoordinateTransformationFactory<MsSqlCoordinate2D> coordTransformFactory
            = new CoordinateTransformationFactory<MsSqlCoordinate2D>(coordFactory, geometryFactory, null);
        ICoordinateSystemFactory<MsSqlCoordinate2D> coordSysFactory
            = new CoordinateSystemFactory<MsSqlCoordinate2D>(coordFactory, geometryFactory);
        IDictionary<TransformCacheKey, IMathTransform> transformCache
            = new Dictionary<TransformCacheKey, IMathTransform>();


        return new Services<MsSqlCoordinate2D>(transformCache, coordFactory, coordSysFactory, geometryFactory, coordTransformFactory);

    }
}